package listeners

import (
	"blockchains-aio/conf"
	"blockchains-aio/util"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
	"os"
	"strconv"
)

func RegisterWallet(router *gin.Engine) {
	router.GET("/wallet/number", getWalletNum)
	router.GET("/wallet", getWallet)

	router.GET("/wallet/number/poa2", getWalletNumForPoa2)
	router.GET("/wallet/poa2", getWalletForPoa2)
}

type TrimGenesis struct {
	Alloc map[string]GenesisAccount `json:"alloc"`
}

type GenesisAccount struct {
	Balance string `json:"balance" gencodec:"required"`
}

func load() error {
	trimGenesis := new(TrimGenesis)
	poa2 := conf.GetGenesisTemplateFolder("poa2")
	genesisPath := fmt.Sprintf("%s/%s", poa2, "genesis.json")
	log.Printf("genesisPath: %s", genesisPath)
	if !util.IsPathExists(genesisPath) {
		return errors.New("fail to load poa2 accounts")
	}

	genesisBytes, _ := os.ReadFile(genesisPath)
	err := json.Unmarshal(genesisBytes, trimGenesis)
	if err != nil {
		log.Println("fail to unmarshal bytes")
		return errors.New("fail to unmarshal bytes")
	}
	log.Printf("trimGenesis.Alloc size: %v", len(trimGenesis.Alloc))
	for k, _ := range trimGenesis.Alloc {
		conf.POA_ETH_ADDRESSES_FROM_GENESIS = append(conf.POA_ETH_ADDRESSES_FROM_GENESIS, k)
	}

	return nil

}

type Poa2Wallet struct {
	AddressesHex string   `json:"addressesHex"`
	Addresses    []string `json:"addresses"`
}

func (poa2Wal *Poa2Wallet) combineAddressesHex() {
	if len(poa2Wal.Addresses) > 0 {
		poa2Wal.AddressesHex = ""

		for _, addr := range poa2Wal.Addresses {
			poa2Wal.AddressesHex = poa2Wal.AddressesHex + addr
		}
	}
}

func (poa2Wal *Poa2Wallet) append(address string) {
	poa2Wal.Addresses = append(poa2Wal.Addresses, address)
}

func (poa2Wal *Poa2Wallet) json() string {
	bytes, _ := json.Marshal(poa2Wal)
	return string(bytes)
}

func getWalletForPoa2(ctx *gin.Context) {
	if len(conf.POA_ETH_ADDRESSES_FROM_GENESIS) == 0 {
		err := load()
		if err != nil {
			ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Wallet, 1, "getWalletNumForPoa2", fmt.Sprintf("fail to execute loading: %v", err)).Json())
			return
		}
	}

	strANumberOfPubkey := ctx.DefaultQuery("ANumberOfPubkey", "0")
	if strANumberOfPubkey == "0" {
		ctx.JSON(http.StatusBadRequest, NewErrorMessage(Wallet, 1, "getWalletForPoa2", fmt.Sprintf("bad parameters: %v", strANumberOfPubkey)).Json())
		return
	}

	number, err := strconv.Atoi(strANumberOfPubkey)
	if err != nil || number < 0 {
		ctx.JSON(http.StatusBadRequest, NewErrorMessage(Wallet, 2, "getWalletForPoa2", fmt.Sprintf("bad parameters: %v", err)).Json())
		return
	}

	if len(conf.POA_ETH_ADDRESSES_FROM_GENESIS) == 0 {
		ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Wallet, 3, "getWalletForPoa2", "POA_ETH_ADDRESSES_FROM_GENESIS is empty after bootstrap").Json())
		return
	}

	poa2Wal := new(Poa2Wallet)
	for index := 0; index < number; index++ {
		poa2Wal.append(conf.POA_ETH_ADDRESSES_FROM_GENESIS[index])
	}
	poa2Wal.combineAddressesHex()

	ctx.JSON(http.StatusOK, poa2Wal.json())
	return
}

func getWalletNumForPoa2(ctx *gin.Context) {
	if len(conf.POA_ETH_ADDRESSES_FROM_GENESIS) == 0 {
		err := load()
		if err != nil {
			ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Wallet, 1, "getWalletNumForPoa2", fmt.Sprintf("fail to execute loading: %v", err)).Json())
			return
		}
	}

	ctx.JSON(http.StatusOK, len(conf.POA_ETH_ADDRESSES_FROM_GENESIS))

	return
}

func getWalletNum(ctx *gin.Context) {
	ctx.JSON(http.StatusOK, len(conf.Eth_Address_PrivateKey_Pairs))

	return
}

func getWallet(ctx *gin.Context) {
	strANumberOfPubkey := ctx.DefaultQuery("ANumberOfPubkey", "0")
	numberOfHWallet := len(conf.HARDCODED_Eth_Address_PrivateKey_Pairs)
	//numberOfWallet := len(conf.Eth_Address_PrivateKey_Pairs)

	if strANumberOfPubkey != "0" {
		var pubkeys []string
		var numberAssignedForHWallet int
		var numberAssignedForWallet int
		number, _ := strconv.Atoi(strANumberOfPubkey)
		if number >= numberOfHWallet {
			numberAssignedForHWallet = numberOfHWallet
			numberAssignedForWallet = number - numberOfHWallet
		} else {
			numberAssignedForHWallet = number
			numberAssignedForWallet = 0
		}

		index := 0
		for k, _ := range conf.HARDCODED_Eth_Address_PrivateKey_Pairs {
			if index == numberAssignedForHWallet {
				break
			}
			pubkeys = append(pubkeys, k)
			index++
		}

		index = 0
		for k, _ := range conf.Eth_Address_PrivateKey_Pairs {
			if index == numberAssignedForWallet {
				break
			}
			pubkeys = append(pubkeys, k)
			index++
		}
		ctx.JSON(http.StatusOK, pubkeys)

		return
	}

	return
}
